package com.fowo.api.sys.component;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fowo.api.sys.annotation.DataActivity;
import com.fowo.api.sys.entity.enums.SysDataActivityAction;
import com.fowo.api.sys.service.SysDataActivityService;
import com.fowo.api.user.model.JwtUserInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.Random;

/**
 * 提供自动数据活动(SysDataActivity)集成
 */
@Aspect
@Component
@Order(-1)
public class SysDataActivityAop {

    @Resource
    private SysDataActivityService sysDataActivityService;

    @Pointcut(value = "@annotation(com.fowo.api.sys.annotation.DataActivity)")
    public void pointcut() {}

    @Around(value = "pointcut() && @annotation(dataActivity)", argNames = "joinPoint, dataActivity")
    public Object around(ProceedingJoinPoint joinPoint, DataActivity dataActivity) throws Throwable {
        if (sysDataActivityService.isDisable(dataActivity.dataSource(), dataActivity.action())) {
            // 已禁用此记录
            return joinPoint.proceed();
        }
        //禁用查询记录
        if (dataActivity.action().equals(SysDataActivityAction.View) ) {
            return joinPoint.proceed();
        }
        final JwtUserInfo jwtUserInfo = JwtUserInfo.fromHeader();
        Object[] args = joinPoint.getArgs();
        if (args.length == 0) {
            if (!StringUtils.hasText(dataActivity.message())) {
                throw new Exception("@DataActivity 注解的方法必须至少包括一个参数！");
            }
        }
        boolean isServiceImpl = ServiceImpl.class.isAssignableFrom(joinPoint.getTarget().getClass());
        ServiceImpl<?,?> serviceImpl = isServiceImpl ? (ServiceImpl<?, ?>) joinPoint.getTarget() : null;
        Class<?> entityClass = dataActivity.entityClass().length > 0 ? dataActivity.entityClass()[0] : (serviceImpl != null ? serviceImpl.getEntityClass() : null);
        if (sysDataActivityService.isDisable(entityClass.getName(), dataActivity.action())) {
            // 已禁用此记录
            return joinPoint.proceed();
        }
        Long id = null;
        Object oldObj = null;
        Object argFirst = args.length > 0 ? args[0] : null;
        if (argFirst != null) {
            if (argFirst.getClass().equals(Long.class)) {
                id = (Long) argFirst;
            } else {
                final Field idField = ReflectionUtils.findField(argFirst.getClass(), "id", Long.class);
                if (idField != null) {
                    idField.setAccessible(true);
                    id = (Long) idField.get(argFirst);
                }
            }
        }
        if (id != null && dataActivity.action() != SysDataActivityAction.Add) {
            if (serviceImpl != null) {
                // oldObj = serviceImpl.getById(id);
                // 2023/12/8 mybatis 一级缓存造成未取到完整数据
                QueryWrapper queryWrapper = QueryWrapper.class.newInstance();
                queryWrapper.eq("id", id);
                // 在查询最后加随机数，让缓存不命中
                Random random = new Random();
                Long r = random.nextLong();
                queryWrapper.last(String.format("AND %d = %d", r, r));
                oldObj = serviceImpl.getOne(queryWrapper);
            }
        }
        try {
            Object obj = joinPoint.proceed();
            final SysDataActivityAopHelper.DataActivityAopContext context = SysDataActivityAopHelper.getContext();
            sysDataActivityService.aopHandleAsync(context, dataActivity, serviceImpl, jwtUserInfo, id, oldObj, obj);
            return  obj;
        } catch (Throwable throwable) {
            throw throwable;
        }
    }
}
